﻿using Asp.Versioning;

using EHO.AutoUpdate.Server.Database;

using GeneralUpdate.AspNetCore.Services;
using GeneralUpdate.Core.Domain.DTO;

using Microsoft.AspNetCore.Mvc;

using NewLife;
using NewLife.Log;

using System.Collections.Generic;
using System.Security.Cryptography;

namespace EHO.AutoUpdate.Server.Controllers.v1
{
    /// <summary>app update</summary>
    [ApiController, ApiVersion(1.0), Route("api/v{v:apiVersion}/[controller]/[action]")]
    public class AppController : ControllerBase
    {
        readonly IUpdateService _UpdateService;
        public AppController(IServiceProvider serviceProvider) => _UpdateService = serviceProvider.GetService<IUpdateService>();

        [HttpGet, Route("{clientType}/{clientVersion}/{clientAppKey}/{sign}")]
        public IActionResult Versions(int clientType, string clientVersion, string clientAppKey, string sign)
        {
            XTrace.Log.Warn($"{clientType}/{clientVersion}/{clientAppKey}/{sign}");
            var request = HttpContext.Request;
            var url = $"{request.Scheme}://{request.Host}/api/v1/App/DownloadFile";
            var app = App.FindByAppKey(clientAppKey);
            if (app == null) { return BadRequest(); }
            var localSign = $"{clientAppKey}{app.Secret}".MD5();
            if (!localSign.Equals(sign, StringComparison.CurrentCultureIgnoreCase)) { return BadRequest(); }
            var version = AppUpdateVersion.FindAll(AppUpdateVersion._.AppId == app.Id & AppUpdateVersion._.ClientType == clientType, page: new NewLife.Data.PageParameter { PageSize = 1, OrderBy = AppUpdateVersion._.Id, Desc = true }).FirstOrDefault();
            var versionDTOs = new List<VersionDTO>();
            var serverLastVersion = version?.Version ?? clientVersion;
            if (version is not null) { versionDTOs.Add(new VersionDTO(version.Md5, version.PubTime, version.Version, $"{url}/{version.Id}", Path.GetFileName(version.Path))); }
            else { versionDTOs.Add(new VersionDTO(string.Empty, 0, serverLastVersion, $"{url}/9999", string.Empty)); }

            return Content(_UpdateService.Update(clientType, clientVersion, serverLastVersion, app.AppKey, clientAppKey, true, versionDTOs));
        }

        [HttpPost, Route("{appId}/{version}")]
        public async Task<IActionResult> UploadFile(long appId, string version, IFormFile file)
        {
            XTrace.Log.Warn($"{appId}/{version}");
            try
            {
                var app = App.FindById(appId);
                var path = "files".CombinePath($"{appId}", $"{DateTime.Now:yyyyMMdd}", file.FileName).GetFullPath();
                using (var fs = new FileStream(path, System.IO.File.Exists(path) ? FileMode.Append : FileMode.OpenOrCreate))
                {
                    await file.CopyToAsync(fs);
                    await fs.FlushAsync();
                    fs.Close();
                    fs.Dispose();
                }
                var appversion = new AppUpdateVersion();
                appversion.AppId = app.Id;
                appversion.PubTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
                appversion.Path = Path.GetRelativePath(".".GetCurrentPath(), path);
                appversion.Md5 = ReadFileMd5(path);
                appversion.Insert();
                return Ok();
            }
            catch (Exception ex) { return BadRequest(ex.Message); }
        }

        [HttpGet, Route("{appversionId}")]
        public IActionResult DownloadFile(long appversionId)
        {
            XTrace.Log.Warn($"{appversionId}");
            var appversion = AppUpdateVersion.FindById(appversionId);
            if (appversion is null) { return BadRequest(); }
            var path = appversion.Path.GetFullPath();
            if (!System.IO.File.Exists(path)) { throw new Exception("文件不存在"); }
            return PhysicalFile(path, "application/octet-stream", Path.GetFileName(path), true);
        }

        string ReadFileMd5(string path)
        {
            using var md5 = MD5.Create();
            using var stream = System.IO.File.OpenRead(path);
            byte[] buffer = new byte[8192]; // Buffer size for reading chunks
            int bytesRead;
            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);
            }
            md5.TransformFinalBlock(buffer, 0, 0); // Finalize the hash calculation
            var hash = md5.Hash.ToHex();
            return hash;
        }
    }
}
